package com.wl.shop.aspect;

import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springside.modules.utils.Exceptions;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.wl.shop.dao.LogDao;
import com.wl.shop.dao.MenuDao;
import com.wl.shop.entity.Log;
import com.wl.shop.entity.Menu;
import com.wl.shop.entity.User;
import com.wl.shop.utils.CacheUtils;
import com.wl.shop.utils.Global;
import com.wl.shop.utils.SpringContextHolder;
import com.wl.shop.utils.StringUtils;
import com.wl.shop.utils.UserUtils;

@Aspect
@Component
public class LogAspect {
	private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
	
	public static final String CACHE_MENU_NAME_PATH_MAP = "menuNamePathMap";
	private static LogDao logDao = SpringContextHolder.getBean(LogDao.class);
	private static MenuDao menuDao = SpringContextHolder.getBean(MenuDao.class);
	
	@Pointcut("within(@org.springframework.stereotype.Controller *)")
    public void cutController() {}
	
	@After("cutController()")  
    public void after(){  
        System.out.println("最终通知");  
    } 
	
	@AfterReturning("cutController()")  
    public void doAfter(){  
        System.out.println("后置通知");  
    } 
	
	@Around("cutController()")
	public Object recordSysLog(ProceedingJoinPoint point) throws Throwable{
		String strMethodName = point.getSignature().getName();
        String strClassName = point.getTarget().getClass().getName();
        Object[] params = point.getArgs();
        StringBuffer bfParams = new StringBuffer();
        Enumeration<String> paraNames = null;
        HttpServletRequest request = null;
        if (params != null && params.length > 0) {
            request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            paraNames = request.getParameterNames();
            String key;
            String value;
            while (paraNames.hasMoreElements()) {
                key = paraNames.nextElement();
                value = request.getParameter(key);
                bfParams.append(key).append("=").append(value).append("&");
            }
            if (StringUtils.isBlank(bfParams)) {
                bfParams.append(request.getQueryString());
            }
        }
        String strMessage = String.format("[类名]:%s,[方法]:%s,[参数]:%s", strClassName, strMethodName, bfParams.toString());
        logger.info(strMessage);
        Object obj = null;
        Exception exception = null;
        if(isWriteLog(strMethodName)){
        	try {
        		obj = point.proceed();
			} catch (Exception e) {
				// TODO: handle exception
				exception = e;
			}
        }
        saveLog(request,exception,strClassName+"-"+strMethodName);
		return obj;
	}
	
	
	
	private boolean isWriteLog(String method) {
        String[] pattern = {"login", "logout", "add", "edit", "delete", "grant"};
        for (String s : pattern) {
            if (method.indexOf(s) > -1) {
                return true;
            }
        }
        return false;
    }
	
	/**
	 * 保存日志
	 */
	private void saveLog(HttpServletRequest request, Exception ex, String title){
		User user = UserUtils.getUser();
		if (user != null && user.getId() != null){
			Log log = new Log();
			log.setTitle(title);
			log.setType(ex == null ? Log.TYPE_ACCESS : Log.TYPE_EXCEPTION);
			log.setRemoteAddr(StringUtils.getRemoteAddr(request));
			log.setUserAgent(request.getHeader("user-agent"));
			log.setRequestUri(request.getRequestURI());
			log.setParams(JSONObject.toJSONString(request.getParameterMap()));
			log.setMethod(request.getMethod());
			// 异步保存日志
			new SaveLogThread(log,ex).start();
		}
	}
	
	/**
	 * 保存日志线程
	 */
	public static class SaveLogThread extends Thread{
		
		private Log log;
		private Exception ex;
		
		public SaveLogThread(Log log, Exception ex){
			super(SaveLogThread.class.getSimpleName());
			this.log = log;
			this.ex = ex;
		}
		
		@Override
		public void run() {
			// 获取日志标题
			if (StringUtils.isBlank(log.getTitle())){
				log.setTitle(getMenuNamePath(log.getRequestUri()));
			}
			// 如果有异常，设置异常信息
			if(ex !=null)	log.setException(Exceptions.getStackTraceAsString(ex));
			
			// 如果无标题并无异常日志，则不保存信息
			if (StringUtils.isBlank(log.getTitle()) && StringUtils.isBlank(log.getException())){
				return;
			}
			if (log.getRequestUri().indexOf("list")!= -1 && "POST".equalsIgnoreCase(log.getMethod())){
				String title = log.getTitle();
				title = title + "-请求数据";
				log.setTitle(title);
			}
			// 保存日志信息
			log.preInsert();
			logDao.insert(log);
		}
	}
	
	/**
	 * 获取菜单名称路径（如：系统设置-机构用户-用户管理-编辑）
	 */
	public static String getMenuNamePath(String requestUri){
		String href = StringUtils.substringAfter(requestUri, Global.getConfig("adminPath"));
		@SuppressWarnings("unchecked")
		Map<String, String> menuMap = (Map<String, String>)CacheUtils.get(CACHE_MENU_NAME_PATH_MAP);
		if (menuMap == null || menuMap.isEmpty()){
			menuMap = Maps.newHashMap();
			List<Menu> menuList = menuDao.selectList(new EntityWrapper<Menu>());
			for (Menu menu : menuList){
				// 获取菜单名称路径（如：系统设置-机构用户-用户管理-编辑）
				String namePath = "";
				if (menu.getParentIds() != null){
					List<String> namePathList = Lists.newArrayList();
					for (String id : StringUtils.split(menu.getParentIds(), ",")){
						if (Menu.getRootId().equals(id)){
							continue; // 过滤跟节点
						}
						for (Menu m : menuList){
							if (m.getId().equals(id)){
								namePathList.add(m.getName());
								break;
							}
						}
					}
					namePathList.add(menu.getName());
					namePath = StringUtils.join(namePathList, "-");
				}
				// 设置菜单名称路径
				if (StringUtils.isNotBlank(menu.getHref())){
					menuMap.put(menu.getHref(), namePath);
				}else if (StringUtils.isNotBlank(menu.getPermission())){
					for (String p : StringUtils.split(menu.getPermission())){
						menuMap.put(p, namePath);
					}
				}
				
			}
			CacheUtils.put(CACHE_MENU_NAME_PATH_MAP, menuMap);
		}
		return menuMap.get(href);
	}
}
